#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <mutex>
#include "log.hpp"
#include "util.hpp"
#include <unordered_map>
#include <boost/algorithm/string.hpp>

namespace ns_index{
	
	// 正排索引结构
	struct DocInfo{
		std::string title;
		std::string content;
		std::string url;
		uint64_t doc_id;
	};

	// 倒排索引
	struct InvertedIndex{
		uint64_t doc_id;
		std::string word;
		int weight;
	};

	// 倒排拉链
	typedef std::vector<InvertedIndex> Invert_t;

	class Index{
		private:
		std::vector<DocInfo> forward_index; // 正排索引列表	
		std::unordered_map<std::string, Invert_t> invert_index; // 关键词 -> 倒排索引列表

		Index(){
			
		}

		Index(const Index&) = delete;
		Index& operator=(const Index&) = delete;

		static Index* instance;
		static std::mutex _mutex;

		public:
		~Index(){

		}

		static Index* GetInstance(){
			_mutex.lock();
			if(instance == nullptr){
				instance = new Index();
			}
			_mutex.unlock();

			return instance;
		}

		// 根据doc_id找到文档内容
		DocInfo* GetForwardIndex(uint64_t doc_id){
			if(doc_id >= forward_index.size()){
				// std::cerr << "文档id越界" << std::endl;
				LOG(ERROR, "文档id越界");
				return nullptr;
			}
			
			return &forward_index[doc_id];
		}

		// 根据关键词找到倒排拉链
		Invert_t* GetInvertedList(const std::string& word){
			auto iter = invert_index.find(word);
			if(iter == invert_index.end()){
				// std::cerr << "关键词不存在" << std::endl;
				LOG(ERROR, "关键词不存在");
				return nullptr;
			}
			// std::cout << "用户查询的关键词为：" << word << std::endl;
			LOG(NORMAL, "用户查询的关键词为：" + word);

			return &(iter->second);
		}

		// 构建索引
		bool BuildIndex(const std::string& input){
			std::ifstream in(input, std::ios::in | std::ios::binary);
			if(!in.is_open()){
				// std::cerr << "打开文件失败" << std::endl;
				LOG(ERROR, "打开文件失败");
				return false;
			}

			std::string file;
			int count = 0;
			while(std::getline(in, file)){
				DocInfo* doc = BuildForward(file);
				if(!doc){
					// std::cerr << "构建正排索引失败" << std::endl;
					LOG(ERROR, "构建正排索引失败");
					continue;
				}

				BuildInverted(*doc);
				count++;

				if(count % 100 == 0){
					// std::cout << "已构建" << count << "条索引" << std::endl;
					LOG(NORMAL, "已构建" + std::to_string(count) + "条索引");
				}
			}

			return true;
		}

		private:
		// 构建正排索引
		DocInfo* BuildForward(const std::string& line){
			// 1.解析line
			std::string sep = "\3";
			std::vector<std::string> result;
			ns_util::StringUtil::StringCut(line, &result, sep);
			
			if(result.size() != 3){
				// std::cerr << "解析结果数量错误" << std::endl;
				LOG(ERROR, "解析结果数量错误");
				return nullptr;
			}

			// 2. 将字符串填充到DocInfo中
			DocInfo doc;
			doc.title = result[0];
			doc.content = result[1];
			doc.url = result[2];
			doc.doc_id = forward_index.size();

			// 3. 将DocInfo添加到正排索引列表中
			forward_index.push_back(std::move(doc));

			return &(forward_index.back());
		}

#define X 10
#define Y 1

		// 构建倒排索引
		bool BuildInverted(DocInfo doc){
			struct word_cnt{
				int title_cnt;
				int content_cnt;
				word_cnt():title_cnt(0), content_cnt(0){}
			};

			std::unordered_map<std::string, word_cnt> word_map;

			// 1.利用jieba库对title进行分词
			std::vector<std::string> title_word;
			ns_util::JiebaUtil::CutString(doc.title, &title_word);

			// 2.统计title中每个词的词频
			for(auto s : title_word){
				boost::to_lower(s); // 转小写
				word_map[s].title_cnt++;
			}


			// 3.利用jieba库对content进行分词
			std::vector<std::string> content_word;
			ns_util::JiebaUtil::CutString(doc.content, &content_word);

			// 4.统计content中每个词的词频
			for(auto s : content_word){
				boost::to_lower(s); // 转小写
				word_map[s].content_cnt++;
			}

			for(auto &word : word_map){
				InvertedIndex invert;
				invert.word = word.first;
				invert.doc_id = doc.doc_id;
				invert.weight = X * word.second.title_cnt + Y * word.second.content_cnt;

				Invert_t &invert_list = invert_index[invert.word];
				invert_list.push_back(std::move(invert));
			}

			return true;
		}
	};

	Index* Index::instance = nullptr;
	std::mutex Index::_mutex;
}